////////////////////////////////////////////////////////////////////////////////////
/* Program: This file contains modules necessary to implement the Decision 
 *			Layer of SENTINEL Project.  Specifically, the Mode Listener and
 *			Send Mode Modules are contained in this file.
 * Author:	Robert Wunderlich
 * Editors: Ryan Bell, Eric Cleveland, Sean Pierce (Team PAINTeK)
 * Purpose: Necessary for the Decision Layer of the SENTINEL system to keep
			track of what mode the system is in.  Accepts socket input from 
 *			User Interface Layer.  Outputs via sockets to the UI Layer and
 *			function calls to the Execution Layer.
 * Misc:	
 */

/* *************** Reference: **************************
	The Winsock code contained in this file was obtained from the MSDN Library:
	http://msdn.microsoft.com/en-us/library/ms740632%28v=vs.85%29

	/** comments were used to represent where function calls to the Execution
	Layer need to be inserted in the final code when the layers are inegrated.

	//D comments were used to remove output that was used for debugging

	//T code represents lines of code that were originally used to create a
	new thread each time activity occurred on the socket for mode changes.
	This code was not deleted in case it becomes necessary to implement a
	separate thread for each mode change event.

	//U comments were used to remove output that was used for Unit Testing

************************************************** */

#ifndef _MODELISTENER_H_
#define _MODELISTENER_H_

#include <winsock2.h>
#include <ws2tcpip.h>
//T#include "CoordinateThread.h"
#include <iostream>
#include <stdio.h>
#include "Thread.h"

#include "DecisionLayerVariable.h"

#define RECEIVE_PORT "5567"
#define SEND_PORT "5568"

#pragma comment(lib, "Ws2_32.lib")

class ModeListener
{
public:
	void Run( DecisionLayerVariable* GV );
	bool IsThreadActive();
	// *********************************************************
	// public test code
	void Test_Listen();

private:
	Lib::Thread<ModeListener, DecisionLayerVariable*> m_thread;
	
	void Listen( DecisionLayerVariable* GV );
};

// ----------------------------------------
void ModeListener::Run( DecisionLayerVariable* GV )
{
	m_thread.Run(this, &ModeListener::Listen, GV);
}

// ----------------------------------------
bool ModeListener::IsThreadActive()
{
	return m_thread.IsThreadActive();
}

// ----------------------------------------
void ModeListener::Listen( DecisionLayerVariable* GV )
/*	The Mode Listener module establishes a socket connection with the UI Layer t
	listen for mode change commands from the GUI Generator and Emergency Stop
	subsystems. Once received, the mode variable is updated and the Send Mode
	module is called.

	The Send Mode Module sends the mode updates to the UI Layer and the Execution
	Layer. This module is called by the Mode Listener when a value is received
	from the UI Layer.
*/
{
	char* IPAddress = "127.0.0.1";

	// set up winsock
	WSADATA wsaData;
	sockaddr_in SenderAddr;
    int SenderAddrSize = sizeof (SenderAddr);

	int error = WSAStartup( MAKEWORD(2,2), &wsaData); // fill in WSA info
	
	if(error != 0) // failed to startup winsock
	{
		std::cout << "WSAStartup failed: " << error << "\n";
	}

	//setup socket to listen for mode changes
	struct addrinfo *result = NULL,
					*ptr = NULL,
					hints;

	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_DGRAM;
	hints.ai_protocol = IPPROTO_UDP;
	hints.ai_flags = AI_PASSIVE;

	// Resolve the local address and port to be used by the server
	error = getaddrinfo(NULL, RECEIVE_PORT, &hints, &result);

	if(error !=0)
	{
		std::cout << "getaddrinfo failed: " << error << "\n";
		WSACleanup();
	}

	SOCKET ListenSocket;

	// Create a SOCKET to listen for mode changes from UI Layer
	ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);

	if(ListenSocket == INVALID_SOCKET)
	{
		std::cout << "Error at socket(): " << WSAGetLastError() << "\n";
		freeaddrinfo(result);
		WSACleanup();
	}

	// Setup the listening socket
	error = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);

	if(error == SOCKET_ERROR)
	{
		std::cout << "bind failed with error: " << WSAGetLastError() << "\n";
		freeaddrinfo(result);
		WSACleanup();
	}

	freeaddrinfo(result);

	// Create a SOCKET to send mode changes to the UI Layer
	int s_error;

	//setup socket to send mode changes
	struct addrinfo *s_result = NULL,
					*s_ptr = NULL,
					s_hints;

	ZeroMemory( &s_hints, sizeof(s_hints) );
	s_hints.ai_family = AF_UNSPEC;
	s_hints.ai_socktype = SOCK_DGRAM;
	s_hints.ai_protocol = IPPROTO_UDP;

	s_error = getaddrinfo(IPAddress, SEND_PORT, &s_hints, &s_result);
	if(s_error != 0)
	{
			std::cout << "getaddrinfo failed: " << s_error << "\n";
			WSACleanup();
	}

	SOCKET ConnectSocket = INVALID_SOCKET;
	
	// Attempt to connect to the first address returned by
	// the call to getaddrinfo
	s_ptr = s_result;

	// Create the sending socket
	ConnectSocket = socket(s_ptr->ai_family, s_ptr->ai_socktype, s_ptr->ai_protocol);

	if(ConnectSocket == INVALID_SOCKET)
	{
		std::cout << "Error at socket(): " << WSAGetLastError() << "\n";
		freeaddrinfo(s_result);
		WSACleanup();
	}
	 
	// Connect to server
	s_error = connect(ConnectSocket, s_ptr->ai_addr, (int)s_ptr->ai_addrlen);
	if(s_error == SOCKET_ERROR)
	{
		closesocket(ConnectSocket);
		ConnectSocket = INVALID_SOCKET;
	}

	
	// Could insert code here to try the next address returned by getaddrinfo
	// if the connect call failed.  Not implementing this for now, so will just
	// free the resources returned by getaddrinfo and print an error message.

	freeaddrinfo(s_result);

	if(ConnectSocket == INVALID_SOCKET)
	{
		std::cout << "Unable to connect to server!";
		WSACleanup();
	}

	// setup variables to be used to read from socket
	int recvbuflen = 10;
	char recvbuf[10];

	// read from socket until the peer shuts down the connection
	do
	{
//D		std::cout << "M: mode: " << GV->mode << " waiting for socket activity...\n";
		error = recvfrom(ListenSocket, recvbuf, recvbuflen, 0, (SOCKADDR *) & SenderAddr, &SenderAddrSize);
		if(error > 0)
		{
			//*mode = recvbuf[0]-'0'; // update mode variable
			GV->mode = recvbuf[0]-'0'; // update mode variable

/**/		//call Execution Layer function to update Mode LED Display
			GV->Sentinel->SendMode( GV->mode );

			s_error = send(ConnectSocket, recvbuf, (int) strlen(recvbuf)+1, 0);
			if(s_error == SOCKET_ERROR)
			{
				std::cout << "send failed: " << WSAGetLastError() << "\n";
				closesocket(ConnectSocket);
				WSACleanup();
			}

//D			std::cout << error << ": " << recvbuf << "\n";
//T			CoordinateThread test1;
//T			test1.Run(recvbuf);
			
			// testing to see if thread runs successfully, giving it
			// time to print a test message
//T			while (test1.IsThreadActive())
//T			::Sleep( 1000 );
		}
		else if(error == 0)
			std::cout << "connection closed\n";
		else
			std::cout << "recv failed: " << WSAGetLastError() << "\n";
	}
	while(error > 0);

	closesocket(ConnectSocket);
	closesocket(ListenSocket);
	WSACleanup();
}


// ****************************************
// PUBLIC TEST FUNCTIONS //
// ----------------------------------------
void ModeListener::Test_Listen()
/*	Test ID: UI3
	This is a test function for the Fire Command module.  If TEST_FC_OHCR_R() is
	set to return "true", then this test should result in the function call to
	fire the gun.  If TEST_FC_OHCR_R() is set to return false, then this test
	should NOT make the function call to fire the gun.
*/
{
	int test_mode = 0;
	//Listen(&test_mode);
}

#endif